import assert from 'assert';

const defaultEntity = {// 树表实体key名标识
  ID: 'id', // 主键id
  PID: 'parent_id', // 父级id
  PIDS: 'parent_ids', // 父级ids
  LEVEL: 'level', // 级别
  CHILDREN: 'children',
  LEAF: 'tree_leaf', // 是否是叶子节点
  TREE_NAMES: 'tree_names', // 全路径名称
  TREE_NAME: 'tree_name', // 节点名称
  DELETE_WAY: 1, // 删除方式：0修改状态，不删除数据;1.删除数据;
  STATUS: 'status'// 状态

};

/**
 *  Tree基类
 */
class TreeService extends think.Service {
  constructor(config) {
    super();
    Object.assign(defaultEntity, config);
  }

  /**
     * 用于树表新增和更新当前节点的所有pids,level,pid,以及所有子子孙孙的pids,　支持任意一级的更新，支持父子节点交换。
     * @param tableName  表名 string
     * @param parentEntity 父对象实体 Object
     * @param childEntity 要保存或更新的子对象实体 Object
     * @param options 选项对象
     */
  async saveTree(tableName, parentEntity, childEntity, options) {
    assert(typeof tableName === 'string', 'tableName must be a string');
    assert(parentEntity instanceof Object, 'parentEntity must be a Object');
    assert(childEntity instanceof Object, 'childEntity must be a Object');
    const tableModel = this.db[tableName];
    const ID = defaultEntity.ID;
    const PID = defaultEntity.PID;
    const PIDS = defaultEntity.PIDS;
    const LEVEL = defaultEntity.LEVEL;
    const TREE_NAMES = defaultEntity.TREE_NAMES;
    const TREE_NAME = defaultEntity.TREE_NAME;
    const TREE_LEAF = defaultEntity.TREE_LEAF;
    // 获取父对象和子对象属性
    const {[ID]: pEntityId, [PID]: pEntityPid, [PIDS]: pEntityPids, [TREE_NAMES]: pEntityTreeNames = ''} = parentEntity;
    const {[ID]: cEntityId, [PID]: cEntityPid, [PIDS]: cEntityPids, [TREE_NAME]: cEntityTreeName = ''} = childEntity;
    // 获取子对象修改前的parentIds和pId，用于更新子节点的parentIds和Pid
    const oldParentIds = cEntityPids;
    const oldParentId = cEntityPid;

    // 如果目标父节点是自己，则不处理PIDS、PID、LEVEL
    if (pEntityId === cEntityId) {
      delete childEntity[PID];
      delete childEntity[PIDS];
      delete childEntity[LEVEL];
      return tableModel.upsert(childEntity);
    }
    // 如果没有设置父节点，则代表为根节点
    if (pEntityPid === null || pEntityPid === '' || pEntityPid === undefined) {
      childEntity[PID] = 0;
      childEntity[PIDS] = '0,';
    } else {
      // 获取父对象实体的parent_ids并且拼接parent_ids
      let updateParentIds = '';
      // 如果存在父子节点交换，踢出在树路径里自己的id
      if (pEntityPids && pEntityPids.includes(cEntityId)) {
        // 更新当前节点的parentIds
        updateParentIds = `${pEntityPids}${pEntityId},`.replace(`${cEntityId},`, '');
      } else {
        updateParentIds = `${pEntityPids}${pEntityId},`;
      }

      childEntity[PIDS] = updateParentIds;
      childEntity[PID] = pEntityId;
      childEntity[LEVEL] = updateParentIds.split(',').length - 1;
    }
    if (cEntityTreeName) {
      // 构造节点全路径名称
      pEntityTreeNames ? childEntity[TREE_NAMES] = pEntityTreeNames + '/' + cEntityTreeName : childEntity[TREE_NAMES] = cEntityTreeName;
    }

    // 如果是新增操作,设置当前节点为末级节点，同时设置父节点为非末级节点
    if (!cEntityId) {
      childEntity[TREE_LEAF] = 1;
      tableModel.update({[TREE_LEAF]: 0}, {
        where: {
          [ID]: pEntityId
        }
      });
    }
    // 保存或更新当前实体
    const upsertResult = tableModel.upsert(childEntity);

    // 更新当前修改节点下所有子节点的parentIds
    if (cEntityId && pEntityPids) {
      assert(oldParentIds != undefined, 'parentIds must be not empty');
      const sql = `SELECT ${ID},${PID},${PIDS} FROM ${tableName} WHERE ${PIDS} like '%${oldParentIds}${cEntityId},%'`;
      const list = await this.CrudService.querySql(sql);
      if (list.length > 0) {
        for (const value of list) {
          if (value[PIDS] && oldParentIds) {
            const obj = {};
            // 如果存在父子节点交换,踢出在树路径里自己的id
            if (pEntityPids.includes(cEntityId)) {
              obj[PIDS] = value[PIDS].replace(`${cEntityId},`, '');
              // 更新交换节点前的pid
              if (value[PID] === cEntityId) {
                obj[PID] = oldParentId;
              }
            } else {
              obj[PIDS] = value[PIDS].replace(oldParentIds, cEntityPids);
            }
            obj[LEVEL] = obj[PIDS].split(',').length - 1;
            tableModel.update(obj, {
              where: {
                id: value[ID]
              }
            });
          }
        }
      }
    }
    return upsertResult;
  }

  /**
     *  全表扫描，用于重新生成全表的PIDS
     *
     * @param tableName  表名 string
     */
  async setAllParentIds(tableName) {
    assert(typeof tableName === 'string', 'tableName must be a string');
    const tableModel = this.db[tableName];
    // 获得表中所有 数据
    const tableDataList = await tableModel.findAll();
    // 将数组构造为id为key的对象
    const idField = defaultEntity.ID;
    const parentIDField = defaultEntity.PID;
    const parentIDSField = defaultEntity.PIDS;
    const dataObj = {};
    tableDataList.map(item => {
      dataObj[item[idField]] = item.dataValues;
    });
    // 遍历数组，并查出parent_ids
    tableDataList.map(item => {
      let parent_ids = item[parentIDField] + ',';
      let isHasParent = true;
      const parent_id = item[parentIDField];
      while (isHasParent) {
        let parentObj = {};
        parentObj = dataObj[parent_id];
        while (parentObj != undefined) {
          parent_ids = parentObj.parent_id + ',' + parent_ids;
          parentObj = dataObj[parentObj.parent_id];
        }
        isHasParent = false;
      }
      item[parentIDSField] = parent_ids;
      return item;
    });
    // 捕捉promise异常，不捕捉经常报ResourceRequest timed out错误
    try {
      for (let i = 0; i < tableDataList.length; i++) {
        await tableModel.update({[parentIDSField]: tableDataList[i].dataValues[parentIDSField]}, {where: {[idField]: tableDataList[i][idField]}});
      }
    } catch (e) {
      console.log(e);
    }
  }

  /**
     *  全表扫描，用于重新生成全表的LEVEL
     *
     * @param tableName  表名 string
     */
  async setLevel(tableName) {
    assert(typeof tableName === 'string', 'tableName must be a string');
    const tableModel = this.db[tableName];
    // 获得表中所有 数据
    const tableDataList = await tableModel.findAll();
    const idField = defaultEntity.ID;
    const parentIDSField = defaultEntity.PIDS;
    const levelField = defaultEntity.LEVEL;
    // 遍历数组，并查出parent_ids
    tableDataList.map(item => {
      const parent_idsArr = item[parentIDSField].split(',');
      item[levelField] = parent_idsArr.length - 1;
    });
    // 捕捉promise异常，不捕捉经常报ResourceRequest timed out错误
    try {
      for (let i = 0; i < tableDataList.length; i++) {
        await tableModel.update({[levelField]: tableDataList[i][levelField]}, {where: {[idField]: tableDataList[i][idField]}});
      }
    } catch (e) {
      console.log(e);
    }
  }

  /**
     * 格式转树状结构
     * @param   {Array}  a 原数据
     */
  transData(a) {
    let r = [],
      hash = {},
      id = defaultEntity.ID,
      pid = defaultEntity.PID,
      children = defaultEntity.CHILDREN,
      i = 0,
      j = 0,
      len = a.length;
    for (; i < len; i++) {
      hash[a[i][id]] = a[i];
    }
    for (; j < len; j++) {
      var aVal = a[j],
        hashVP = hash[aVal[pid]];
      if (hashVP) {
        !hashVP[children] && (hashVP[children] = []);
        hashVP[children].push(aVal);
      } else {
        r.push(aVal);
      }
    }
    return r;
  }

  /**
     * 根据表名和ID获取当前节点的子子孙孙
     * @param tableName  String  表名 (必传)
     * @param id      String   id  (必传)
     * @param fields Array  fields,默认['*']
     */
  findGrandchildrens(tableName, id, fields = ['*']) {
    const {ID, PIDS} = defaultEntity;
    fields = fields.join();
    const sql = `SELECT ${fields} FROM ${tableName} WHERE ${PIDS} LIKE CONCAT('%', (SELECT ${PIDS} FROM ${tableName} WHERE ${ID}=${id}),'${id},' '%')`;
    return this.CrudService.querySql(sql);
  }

  /**
     * 根据表名和ID获取当前节点的子子孙孙（包含自己）
     * @param tableName  String  表名 (必传)
     * @param id      String   id  (必传)
     * @param fields Array  fields,默认['*']
     */
  findGrandchildrensAndSelf(tableName, id, fields = ['*']) {
    const {ID, PIDS} = defaultEntity;
    fields = fields.join();
    const sql = `SELECT ${fields} FROM ${tableName} WHERE ${PIDS} LIKE CONCAT('%', (SELECT ${PIDS} FROM ${tableName} WHERE ${ID}=${id}),'${id},' '%') or ${ID}=${id}`;
    return this.CrudService.querySql(sql);
  }

  /**
     * 根据表名和ID获取当前节点下的所有子节点(不包含孙孙子子)
     */
  findChildrens(tableName, id, fields = ['*']) {
    const {ID, PIDS, PID} = defaultEntity;
    fields = fields.join();
    const sql = `SELECT ${fields} FROM ${tableName} WHERE ${PID}=${id}`;
    return this.CrudService.querySql(sql);
  }

  /**
     * 根据表名和ID删除当前节点及其下面的所有子节点
     */
  async deleteParentAndChildrenNode(tableName, id) {
    assert(typeof tableName === 'string', 'tableName must be a string');
    const tableModel = this.db[tableName];
    const {ID, PIDS, DELETE_WAY, STATUS} = defaultEntity;
    const Op = this.db.sequelize.Op;
    try {
      if (DELETE_WAY) {
        await tableModel.destroy({
          where: {
            [Op.or]: {
              [PIDS]: {
                [Op.like]: '%' + id + ','
              },
              [ID]: id
            }
          }
        });
      } else {
        await tableModel.update({[STATUS]: 1}, {
          where: {
            [Op.or]: {
              [PIDS]: {
                [Op.like]: '%' + id + ','
              },
              [ID]: id
            }
          }
        });
      }
    } catch (error) {
      return this
        .exceptionService
        .handleError(error);
    }
  }
}

export default TreeService;
